#!/usr/bin/python -tt
#
# Dominik Neise
#
import os.path
import numpy as np
import math
from euclid import *
import sys

class Coordinator(object):
    """ class to transform chid <-> hexagonal coordinates and vice versa """

    def __init__(self, map_file_path = "map.txt"):
        """ read map text file and generate from the three columns 
            chid, xe and ye
            3 dictionaries:  chid2coor, coor2chid, chid2nn  
            chid2nn means 'chid_to_next_neighbor_chids'
            
            this is done by calculating the hexagonal coordinates
            from the euclidian coordinates given in xe & ye.
            the center and the two base vectors are hard coded to be:
            center = Vector2( 0. , 1./2.)
            ey = Vector2( 0. , 1. )
            ex = Vector2( sqrt(3)/2. , 1./2. )
        """
        _CheckPath(map_file_path)
        path = os.path.abspath(__file__)
        path = os.path.dirname(path)
        map_file_path = os.path.join(path, map_file_path)
        chid, xe, ye = np.loadtxt(map_file_path, unpack=True)
        # -ye in order to correct for the sign difference between my mapping file 
        # and FACTmap111030.txt
        self.x = xe
        self.y = ye
        coors = zip(xe,-ye,chid)
        
        # this list will contain vectors pointing to the center of pixel 
        # in euclidian space. The coordinate system is in the focalplane of the 
        # camera, the unit is not mm, but something like 9mm.
        # actually the list will not only contain these vectors, but also 
        # also the CHID of the according pixel, both bundled in a tuple
        vectors_and_chids = []
        for c in coors:
            vectors_and_chids.append( (Vector2(c[0], c[1]) , int(c[2])) )

        # In the next few lines, I will calculate hexagonal coordinates from 
        # the euclidian coordinates. The reason is, that I like to work with 
        # integers.
        # I could have read these numbers from a file instead of calculating,
        # but this is error prone, because one has to make sure, the different
        # coordinates in a file are always conincident.
        
        # The center of the coordinate system is not 0. / 0. since there
        # is not pixel :-) We decided to define the upper one of the two 
        # central pixels, as 'The Center' :-)
        center = Vector2( 0. , 1./2.)
        # the y-axis goes up
        ey = Vector2( 0. , 1. )
        # but the x-axis is turned 30degrees up, wrt the euclidian x-axis.
        ex = Vector2( math.sqrt(3)/2. , 1./2. )
        self.center = ( center.x , center.y )
        self.ey =  ( ey.x, ey.y)
        self.ex = ( ex.x, ex.y )

        # these dicts will serve as translators,
        # e.g. put a chid into chid2coor and you get a Vector2 out, which points
        # to the center of the according pixel.
        coor2chid = {}
        chid2coor = {}
        chid2coor_np = {}
        chid2vec  = {}
        euc2chid = {}
        # we will fill these translators now.
        for vector_and_chid in vectors_and_chids:
            vec = vector_and_chid[0]
            chid = vector_and_chid[1]
            
            # translating from euclidian into hexagonal
            # coordinates here...
            x = (vec-center).x / float(ex.x)
            y = ((vec-center)-x*ex).y / float(ey.y)
        
            # I want them to be integer, so I think I have to
            # treat the values, which are almost zero special,
            # but maybe rounding is just sufficient, as it is done
            # in the line after these...
            if abs(x) < 0.01:
                x=0.0
            if abs(y) < 0.01:
                y=0.0

            # okay, now coor, is the hexagonal coordinate pair of the current pixel
            # as a tuple
            coor = (int(round(x)),int(round(y)))
            # as Vector2
            coor_vec = Vector2(coor[0], coor[1])
            
            # since we just calculated this coordinate, we should make
            # sure, that we did not make an error such, that two pixels have the 
            # same coordinates
            # other errors like holes in the camera plane cannot be detected so easily
            if coor in coor2chid:
                print 'error while filling "coor2chid":'
                print 'coor:',coor,'of chid:',chid,
                print 'is equal to coor of chid:',coor2chid[coor]

            # now we fill the translators
            chid2coor[ chid ] = coor
            chid2coor_np[ chid ] = np.array(coor)
            chid2vec[ chid ] = coor_vec
            # this translator is hardly used by people, but the next step
            # the calculation of the neighbors needs it
            coor2chid[ coor ] = chid
            euc2chid[(vec[0],vec[1])] = chid


        # hard code the offsets to the next neighbors
        # in hexagonal coordinates, the coordinates of neighbors are easily calculated.
        # just add one of the Vectors below.
        offsets = [ Vector2(1,0) ,      # right and up
                    Vector2(-1,0) ,     # left and down
                    Vector2(1,-1) ,     # right and down
                    Vector2(0,1) ,      # up
                    Vector2(0,-1) ,     # down
                    Vector2(-1,1) ]     # left and up
        # this dict serves as a neighbor look up table
        # put a CHID in and get a list of neighboring CHIDs out.
        chid2nn = {}
        for chid in chid2coor.keys():
            coor = Vector2( chid2coor[chid][0] , chid2coor[chid][1] )
            nn_coors = []
            nn_chids = []
            for offset in offsets:
                nn_coors.append( ((coor+offset).x , (coor+offset).y) )
            for coor in nn_coors:
                if coor in coor2chid:
                    nn_chids.append( coor2chid[coor] )
            chid2nn[chid] = nn_chids
        self.nn = chid2nn
        
        self.chid2coor = chid2coor
        self.chid2coor_np = chid2coor_np
        self.coor2chid = coor2chid
        self.chid2vec  = chid2vec
        self.euc2chid = euc2chid
        
#        for chid in chid2nn.keys():
#            print chid, '->',chid2nn[chid]

    def get_geom_coors(self, x ,y ): 
        vec = np.array([x,y])
        center = np.array([self.center[0], self.center[1]])
        ex = np.array([self.ex[0], self.ex[1]])
        ey = np.array([self.ey[0], self.ey[1]])
        
        x = (vec-center)[0] / float(ex[0])
        y = ((vec-center)-x*ex)[1] / float(ey[1])
        
        print "x:", x
        print "y:", y
    
        xmin = int(np.floor(x))
        xmax = int(np.ceil(x))
        ymin = int(np.floor(y))
        ymax = int(np.ceil(y))
        
        a = (xmin,ymin)
        b = (xmin,ymax)
        c = (xmax,ymin)
        d = (xmax,ymax)
        
        return a,b,c,d
        
    def get_euc_coors_from_chid( self, chid ):
        center = np.array([self.center[0], self.center[1]])
        ex = np.array([self.ex[0], self.ex[1]])
        ey = np.array([self.ey[0], self.ey[1]])
        
        coors = self.chid2coor[chid]
        
        euc = center + coors[0]*ex + coors[1]*ey 
        return euc
    
    def wasweissich(self, x ,y ): 
        lala = self.get_geom_coors(x,y)
        bamm = map(self.coor2chid.__getitem__,lala)
        return map(self.get_euc_coors_from_chid,bamm)
        
def first(a):
    return a[0]

def second(a):
    return a[1]


def _CheckPath( inpath ):
    path = os.path.abspath(__file__)
    path = os.path.dirname(path)
    inpath = os.path.join(path, inpath)
    if not os.path.isfile(inpath):
        raise IOError('not able to find file: '+inpath)

